use std::collections::HashMap;
use std::path::PathBuf;
use crate::core::BuildConfiguration;
use crate::config::{EnvironmentVariablesConfig };

/// Visual Studio 环境变量解析器
/// 处理如 $(QTDIR)、$(Configuration)、$(Platform) 等环境变量的解析和转换
/// 
/// 解析优先级：
/// 1. 配置文件中的显式映射（最高优先级）
/// 2. 系统环境变量
/// 3. 自动检测（如Qt路径检测）
/// 4. 默认值（最低优先级）
#[derive(Debug, Clone)]
pub struct EnvironmentResolver {
    /// 配置中的环境变量设置
    config: EnvironmentVariablesConfig,
    /// 预定义的环境变量映射
    variable_mappings: HashMap<String, String>,
    /// 项目根目录，用于解析相对路径
    project_root: PathBuf,
}

impl EnvironmentResolver {
    pub fn new(project_root: PathBuf) -> Self {
        let config = EnvironmentVariablesConfig::default();
        Self::with_config(project_root, config)
    }
    
    pub fn with_config(project_root: PathBuf, config: EnvironmentVariablesConfig) -> Self {
        let mut resolver = Self {
            config,
            variable_mappings: HashMap::new(),
            project_root,
        };
        
        // 初始化常见的环境变量映射
        resolver.init_default_mappings();
        resolver
    }

    /// 初始化默认的环境变量映射
    fn init_default_mappings(&mut self) {
        // Qt 相关环境变量 - 使用延迟解析，不在初始化时设置QTDIR
        
        // 平台相关映射
        self.set_variable("Platform", "x64".to_string());
        self.set_variable("PlatformToolset", "v143".to_string());
        
        // Visual Studio 路径映射 - 使用相对路径，让 xmake 自动处理
        self.set_variable("MSBuildProjectDirectory", ".".to_string());
        self.set_variable("ProjectDir", ".".to_string());
        self.set_variable("SolutionDir", "..".to_string());
        
        // 输出目录映射
        self.set_variable("OutDir", "build/$(config)/$(arch)/".to_string());
        self.set_variable("IntDir", "build/$(config)/$(arch)/obj/".to_string());
    }

    /// 设置环境变量
    pub fn set_variable(&mut self, name: &str, value: String) {
        self.variable_mappings.insert(name.to_string(), value);
    }

    /// 解析字符串中的环境变量
    /// 将 $(VarName) 格式的变量替换为对应的值
    pub fn resolve_variables(&self, input: &str, config: &BuildConfiguration) -> String {
        let mut result = input.to_string();
        
        // 处理配置相关的特殊变量
        result = self.resolve_config_variables(&result, config);
        
        // 使用正则表达式查找所有 $(VarName) 格式的变量
        let re = regex::Regex::new(r"\$\(([^)]+)\)").unwrap();
        
        // 一次性处理所有变量，避免无限循环
        let mut final_result = result.clone();
        
        for captures in re.captures_iter(&result) {
            let full_match = &captures[0];
            let var_name = &captures[1];
            
            if let Some(replacement) = self.get_variable_value(var_name) {
                final_result = final_result.replace(full_match, &replacement);
                // log::debug!("替换变量 {} -> {}", full_match, replacement);
            } else {
                // 如果找不到映射，保留原始变量不替换
                log::warn!("未知的环境变量: $({}), 保持原样", var_name);
            }
        }
        
        final_result
    }

    /// 解析路径列表中的环境变量
    /// 支持按分号拆分多路径，包括环境变量内包含的多路径
    pub fn resolve_path_list(&self, paths: &str, config: &BuildConfiguration) -> Vec<PathBuf> {
        // 先按分号分割输入字符串
        let initial_paths: Vec<&str> = paths
            .split(';')
            .map(|s| s.trim())
            .filter(|s| !s.is_empty() && !s.starts_with('%')) // 过滤掉 %(Inherit) 这类宏
            .collect();

        let mut result_paths = Vec::new();

        for path_str in initial_paths {
            let resolved = self.resolve_variables(path_str, config);
            
            // 检查解析后的结果是否包含分号分隔的多路径
            if resolved.contains(';') {
                // 如果包含分号，说明这是一个多路径值，需要进一步拆分
                for sub_path in resolved.split(';') {
                    let sub_path = sub_path.trim();
                    if !sub_path.is_empty() && !sub_path.starts_with('%') {
                        result_paths.push(self.normalize_path(sub_path));
                    }
                }
            } else {
                // 单一路径，直接添加
                result_paths.push(self.normalize_path(&resolved));
            }
        }

        result_paths
    }

    /// 解析单个环境变量并按分号拆分为多路径
    /// 专门用于处理像 $(AllReleaseLibPaths) 这样可能包含多个路径的环境变量
    pub fn resolve_variable_as_paths(&self, var_name: &str, config: &BuildConfiguration) -> Vec<PathBuf> {
        if let Some(value) = self.get_variable_value(var_name) {
            let resolved_value = self.resolve_variables(&value, config);
            
            // 按分号分割路径
            resolved_value
                .split(';')
                .map(|s| s.trim())
                .filter(|s| !s.is_empty() && !s.starts_with('%'))
                .map(|s| self.normalize_path(s))
                .collect()
        } else {
            Vec::new()
        }
    }

    /// 处理配置相关的特殊变量
    fn resolve_config_variables(&self, input: &str, config: &BuildConfiguration) -> String {
        let mut result = input.to_string();
        
        // 替换配置相关变量
        let config_name = match config {
            BuildConfiguration::Debug => "Debug",
            BuildConfiguration::Release => "Release", 
            BuildConfiguration::Custom(name) => name,
        };
        
        result = result.replace("$(Configuration)", config_name);
        result = result.replace("$(ConfigurationName)", config_name);
        
        // 常见的配置组合
        let platform_config = format!("x64_{}", config_name);
        result = result.replace("$(Platform)_$(Configuration)", &platform_config);
        
        let toolset_config = format!("v143_{}", config_name);
        result = result.replace("$(PlatformToolset)_$(Configuration)", &toolset_config);
        
        let full_config = format!("x64_v143_{}", config_name);
        result = result.replace("$(Platform)_$(PlatformToolset)_$(Configuration)", &full_config);
        
        result
    }

    /// 获取环境变量的值，按优先级查找
    /// 1. 配置文件映射 -> 2. 系统环境变量 -> 3. 预定义映射
    fn get_variable_value(&self, name: &str) -> Option<String> {
        // 1. 首先检查配置文件中的显式映射
        if let Some(value) = self.config.mappings.get(name) {
            // log::debug!("从配置文件获取环境变量 {}: {}", name, value);
            return Some(value.clone());
        }
        
        // 2. 检查系统环境变量
        if self.config.use_system_env {
            if let Ok(value) = std::env::var(name) {
                log::debug!("从系统环境变量获取 {}: {}", name, value);
                return Some(value);
            }
        }
        
        // 3. 使用预定义映射
        if let Some(value) = self.variable_mappings.get(name) {
            log::debug!("使用预定义映射 {}: {}", name, value);
            return Some(value.clone());
        }
        
        None
    }

    
    
    

    /// 规范化路径，转换为相对路径
    fn normalize_path(&self, path_str: &str) -> PathBuf {
        let path = PathBuf::from(path_str);
        
        // 如果是绝对路径，尝试转换为相对路径
        if path.is_absolute() {
            if let Ok(relative) = path.strip_prefix(&self.project_root) {
                relative.to_path_buf()
            } else {
                path
            }
        } else {
            path
        }
    }


    /// 更新配置
    pub fn update_config(&mut self, config: EnvironmentVariablesConfig) {
        self.config = config;
    }
    
    /// 获取当前配置
    pub fn get_config(&self) -> &EnvironmentVariablesConfig {
        &self.config
    }

    /// 从项目文件中提取和更新环境变量信息
    pub fn update_from_project_properties(&mut self, properties: &HashMap<String, String>) {
        // 从项目属性中提取环境变量信息
        for (key, value) in properties {
            match key.as_str() {
                "PlatformToolset" => {
                    self.set_variable("PlatformToolset", value.clone());
                }
                "WindowsTargetPlatformVersion" => {
                    self.set_variable("WindowsTargetPlatformVersion", value.clone());
                }
                _ => {}
            }
        }
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::tempdir;

    #[test]
    fn test_resolve_basic_variables() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        resolver.set_variable("QTDIR", "./qt".to_string());
        
        let input = "$(QTDIR)/bin/moc.exe";
        let result = resolver.resolve_variables(input, &BuildConfiguration::Debug);
        
        assert_eq!(result, "./qt/bin/moc.exe");
    }

    #[test]
    fn test_resolve_configuration_variables() {
        let temp_dir = tempdir().unwrap();
        let resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        
        let input = "build/$(Configuration)/$(Platform)";
        let result = resolver.resolve_variables(input, &BuildConfiguration::Debug);
        
        assert_eq!(result, "build/Debug/x64");
    }
    #[test]
    fn test_complex_variable_combination() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        resolver.set_variable("OutDir", "build/debug/x64/".to_string());
        
        let input = "$(OutDir)$(Platform)_$(PlatformToolset)_$(Configuration)";
        let result = resolver.resolve_variables(input, &BuildConfiguration::Release);
        
        assert!(result.contains("Release"));
        assert!(result.contains("x64"));
        assert!(result.contains("v143"));
        assert!(result.contains("build/debug/x64/"));
    }

    #[test] 
    fn test_update_from_project_properties() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        
        let mut properties = HashMap::new();
        properties.insert("PlatformToolset".to_string(), "v142".to_string());
        
        resolver.update_from_project_properties(&properties);
        
        let toolset_result = resolver.resolve_variables("$(PlatformToolset)", &BuildConfiguration::Debug);
        
        assert_eq!(toolset_result, "v142");
    }

    #[test]
    fn test_unknown_variables_kept_unchanged() {
        let temp_dir = tempdir().unwrap();
        let resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        
        let input = "$(UnknownVariable)/path";
        let result = resolver.resolve_variables(input, &BuildConfiguration::Debug);
        
        // 未知变量应该保持原样
        assert_eq!(result, "$(UnknownVariable)/path");
    }

    #[test]
    fn test_resolve_multi_path_environment_variable() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        
        // 设置一个包含多路径的环境变量
        resolver.set_variable("AllReleaseLibPaths", "C:/lib1;C:/lib2;C:/lib3".to_string());
        
        // 测试解析包含多路径环境变量的路径列表
        let paths = "$(QTDIR)/lib;$(AllReleaseLibPaths);./local/lib";
        let result = resolver.resolve_path_list(paths, &BuildConfiguration::Release);
        
        // 应该得到 5 个路径：QTDIR/lib (保持不变), C:/lib1, C:/lib2, C:/lib3, ./local/lib
        assert_eq!(result.len(), 5);
        
        let path_strings: Vec<String> = result.iter()
            .map(|p| p.to_string_lossy().to_string())
            .collect();
        
        // 检查各个路径
        assert!(path_strings.iter().any(|p| p.contains("$(QTDIR)") && p.contains("lib")));
        assert!(path_strings.iter().any(|p| p.contains("lib1")));
        assert!(path_strings.iter().any(|p| p.contains("lib2")));
        assert!(path_strings.iter().any(|p| p.contains("lib3")));
        assert!(path_strings.iter().any(|p| p.contains("local") && p.contains("lib")));
    }

    #[test]
    fn test_resolve_variable_as_paths() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        
        // 设置包含多路径的环境变量
        resolver.set_variable("AllReleaseLibPaths", "C:/path1;D:/path2;E:/path3".to_string());
        
        let result = resolver.resolve_variable_as_paths("AllReleaseLibPaths", &BuildConfiguration::Release);
        
        assert_eq!(result.len(), 3);
        let path_strings: Vec<String> = result.iter()
            .map(|p| p.to_string_lossy().to_string())
            .collect();
        
        assert!(path_strings.iter().any(|p| p.contains("path1")));
        assert!(path_strings.iter().any(|p| p.contains("path2")));
        assert!(path_strings.iter().any(|p| p.contains("path3")));
    }

    #[test]
    fn test_resolve_variable_as_paths_with_nested_variables() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        
        // 设置包含嵌套环境变量的多路径变量
        resolver.set_variable("BaseLibPath", "C:/base".to_string());
        resolver.set_variable("AllReleaseLibPaths", "$(BaseLibPath)/lib1;$(BaseLibPath)/lib2;D:/external".to_string());
        
        let result = resolver.resolve_variable_as_paths("AllReleaseLibPaths", &BuildConfiguration::Release);
        
        assert_eq!(result.len(), 3);
        let path_strings: Vec<String> = result.iter()
            .map(|p| p.to_string_lossy().to_string())
            .collect();
        
        // 检查嵌套变量是否被正确解析
        assert!(path_strings.iter().any(|p| p.contains("base") && p.contains("lib1")));
        assert!(path_strings.iter().any(|p| p.contains("base") && p.contains("lib2")));
        assert!(path_strings.iter().any(|p| p.contains("external")));
    }

    #[test]
    fn test_resolve_path_list_with_empty_and_inherit_paths() {
        let temp_dir = tempdir().unwrap();
        let mut resolver = EnvironmentResolver::new(temp_dir.path().to_path_buf());
        
        // 设置包含空路径和%(Inherit)宏的环境变量
        resolver.set_variable("TestPaths", "C:/valid1;;%(AdditionalLibraryDirectories);C:/valid2".to_string());
        
        let paths = "$(TestPaths);./local";
        let result = resolver.resolve_path_list(paths, &BuildConfiguration::Debug);
        
        // 应该过滤掉空路径和%(...)宏，只留下有效路径
        assert_eq!(result.len(), 3); // C:/valid1, C:/valid2, ./local
        
        let path_strings: Vec<String> = result.iter()
            .map(|p| p.to_string_lossy().to_string())
            .collect();
        
        assert!(path_strings.iter().any(|p| p.contains("valid1")));
        assert!(path_strings.iter().any(|p| p.contains("valid2")));
        assert!(path_strings.iter().any(|p| p.contains("local")));
        assert!(!path_strings.iter().any(|p| p.contains("%("))); // 不应该包含宏
    }
}